package com.jspx.txweb.service.web;

import com.jspx.boot.EnvFactory;
import com.jspx.boot.JspxNetApplication;
import com.jspx.boot.environment.EnvironmentTemplate;
import com.jspx.boot.sign.AuditingType;
import com.jspx.boot.sign.LanguageRes;
import com.jspx.txweb.dispatcher.service.RocService;
import com.jspx.txweb.dispatcher.service.RsaRocService;
import com.jspx.txweb.dao.ConnectAppDAO;
import com.jspx.txweb.table.ConnectApp;
import com.jspx.txweb.util.RequestUtil;
import com.jspx.txweb.view.AuthenticationView;
import org.slf4j.Logger;
import com.jspx.boot.environment.Environment;
import org.slf4j.LoggerFactory;
import com.jspx.json.JSONException;
import com.jspx.json.JSONObject;
import com.jspx.json.XML;
import com.jspx.security.utils.EncryptUtil;
import com.jspx.sioc.BeanFactory;
import com.jspx.txweb.IUserSession;
import com.jspx.txweb.bundle.Bundle;
import com.jspx.txweb.dispatcher.Dispatcher;
import com.jspx.txweb.env.TXWeb;
import com.jspx.txweb.env.TXWebIoc;
import com.jspx.txweb.online.OnlineManager;
import com.jspx.txweb.service.WebBeanFactory;
import com.jspx.txweb.support.ActionSupport;
import com.jspx.txweb.util.TXWebUtil;
import com.jspx.utils.*;
import org.slf4j.helpers.MessageFormatter;
import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by chenyuan on 2015-7-8.
 *
 *这里是一个 jax-ws 方式的WebService 调用接口
 * sun-jaxws.xml 配置如下
 * <?xml version="1.0" encoding="UTF-8"?>
 * <endpoints  xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"  version="2.0">
 *     <endpoint name="JspxNetWebFactory"
 * 		implementation="com.jspx.txweb.service.web.JspxNetWebFactoryImpl"
 * 		interface="com.jspx.txweb.service.web.WebBeanFactoryAPI"
 * 		url-pattern="/services/jspxnetwebfactory"/>
 * </endpoints>
 *
 *  AopEndpointPublishServices 是启动发布功能
 *
 *  <bean id="aopEndpointPublishServices" class="com.jspx.sioc.aop.AopEndpointPublishServices" singleton="false" >
 *  <string name="host">http://xxxxxxxxxxxxxxxxxxxxxxx</string>
 *  </bean>
 *
 *  <bean id="aopBootBean" class="com.jspx.sioc.aop.AopAppCommandImpl" singleton="true">
 *  <boolean name="enable">${aopboot}</boolean>
 *  <array name="beanArray" class="string">
 *  <value>aopEndpointPublishServices</value>
 *  </array>
 *  </bean>
 *
 * 配置好后，使用jdk自带工具wsimport生成客户端：
 *  wsimport -d ./bin -s ./src http://xxxxxxxxxxxxxxxxxxxxxxx
 *
 * eg: wsimport -s . http://127.0.0.1/service/jspxnetwebfactory?wsdl
 *  然后就可以调用了，
 *  soap方式，就是接口描述比较清晰，时候第三放调用，但是要麻烦一点。*  好像端口不能公用80，必须单独的端口发布
 *  hessian方便一点，只是接口要单独发布给对方，不能自动生成
 *
 *  soap接口方式，需要String 来返回给用户，不能直接打印到 request
 *
 *
 */
@WebService(serviceName = "jspxnetwebfactory")
public class JspxNetWebFactoryImpl extends AuthenticationView implements WebBeanFactory {
    private static final Logger log = LoggerFactory.getLogger(JspxNetWebFactoryImpl.class);
    private EnvironmentTemplate envTemplate = EnvFactory.getEnvironmentTemplate();
    private OnlineManager onlineManager;
    private ConnectAppDAO connectAppDAO;
    private String sessionId;
    public JspxNetWebFactoryImpl()
    {
        JspxNetApplication.autoRun();
        BeanFactory beanFactory = EnvFactory.getBeanFactory();
        setConfig((Bundle) beanFactory.getBean(Environment.config, "user"));
        setLanguage((Bundle) beanFactory.getBean(Environment.language, "user"));
        onlineManager = ((OnlineManager) beanFactory.getBean(TXWebIoc.onlineManager));
        connectAppDAO = (ConnectAppDAO)beanFactory.getBean(Environment.connectAppDAO);
    }

    private Map<String, Object> createEnvironment() {
        Map<String, Object> venParams = new HashMap<String, Object>();
        venParams.put(Environment.filterSuffix, envTemplate.getString(Environment.filterSuffix));
        venParams.put(Environment.templateSuffix, envTemplate.getString(Environment.templateSuffix));
        venParams.put(Environment.encode, Dispatcher.getEncode());
        venParams.put(Environment.remoteHostUrl, envTemplate.getString(Environment.remoteHostUrl, ""));
        venParams.put(Environment.scriptPath, envTemplate.getString(Environment.scriptPath, "/script"));
        venParams.put(Environment.sitePath, envTemplate.getString(Environment.sitePath, "/"));
        venParams.put(Environment.userLoginUrl, envTemplate.getString(Environment.userLoginUrl, "/user/index."+envTemplate.getString(Environment.filterSuffix)));
        venParams.put(Environment.logJspxDebug, envTemplate.getBoolean(Environment.logJspxDebug));
        venParams.put("date", new Date());
        return venParams;
    }


    @Resource
    private WebServiceContext wsContext;


    @WebMethod
    public void setSessionId(String sessionId)
    {
        this.sessionId = sessionId;
    }


    @WebMethod
    public boolean isOnline() throws Exception {
        if (StringUtil.isNULL(sessionId)) return false;
        IUserSession userSession  = onlineManager.getUserSession(sessionId);
        return userSession != null && !userSession.isGuest();
    }


    @WebMethod
    public void exit()
    {
        MessageContext messageContext = wsContext.getMessageContext();
        setRequest((HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST));
        setResponse((HttpServletResponse) messageContext.get(MessageContext.SERVLET_RESPONSE));
        try {
            if (session!=null&&session.getAttribute(TXWeb.sessionId)==null)
                session.setAttribute(TXWeb.sessionId,sessionId);
            onlineManager.exit(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @WebMethod
    public String getNetPublicKey() {
        MessageContext messageContext = wsContext.getMessageContext();
        setRequest((HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST));
        setResponse((HttpServletResponse)messageContext.get(MessageContext.SERVLET_RESPONSE));
        String publicKeyHost = StringUtil.trim(config.getString(Environment.publicKeyHost));
        if (!IpUtil.interiorly(publicKeyHost,getRemoteAddr()))
        {
            return language.getLang(LanguageRes.notAllowedIpLimits);
        }
        int publicKeyHour = config.getInt(Environment.publicKeyHour,1);
        if  (publicKeyHour<=0)
        {
            publicKeyHour = 2;
            try {
                config.save(Environment.publicKeyHour,publicKeyHour + StringUtil.empty);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (StringUtil.isNULL(TXWeb.publicKey) || System.currentTimeMillis()- TXWeb.publicKeyCreateTimeMillis>DateUtil.HOUR*publicKeyHour)
        {
            TXWeb.publicKey = EncryptUtil.getMd5(System.currentTimeMillis() + RandomUtil.getRandomNumeric(32));
            TXWeb.publicKeyCreateTimeMillis = System.currentTimeMillis();
            try {
                config.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return TXWeb.publicKey;
    }

    /**
     *
     * @return 时间校对，避免出现超时登陆,必须先登录
     */
    @WebMethod
    public long getCurrentTimeMillis() {
        try {
            if (isOnline()) return System.currentTimeMillis();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }


    /**
     * 接口说明，先创建 ConnectApp 表，然后得到appId,审核通过后得到秘钥（私密）， 通过URL地址得到公米，根据算法得到验证码
     * @param appId  申请的应用ID
     * @param loginId  用户ID
     * @param password  用户密码
     * @param timeMillis  验证时间 java  System.currentTimeMillis()  误差限制在5分钟内
     * @param verify 验证码
     * @return  得到sessionId  通过它再去完成其他操作
     * @throws Exception  异常
     */
    @WebMethod
    public String login(long appId,String loginId,String password,int passwordEncoded,long timeMillis,String verify) throws Exception
    {
        if (appId<=0)
        {
            return TXWebUtil.createRocErrorInfo("login",-32602,language.getLang(LanguageRes.invalidParameter)+":appId").toString(4);
        }
        if (StringUtil.isNULL(verify))
        {
           return TXWebUtil.createRocErrorInfo("login",-32602,language.getLang(LanguageRes.invalidParameter)+":verify").toString(4);
        }
        long verifySecond = System.currentTimeMillis() - timeMillis;
        if (Math.abs(verifySecond) >DateUtil.MINUTE*5)
        {
            return TXWebUtil.createRocErrorInfo("login",-32602,language.getLang(LanguageRes.authenticationTimeOut)+"," + verifySecond).toString(4);
        }

        MessageContext messageContext = wsContext.getMessageContext();
        setRequest((HttpServletRequest)messageContext.get(MessageContext.SERVLET_REQUEST));
        setResponse((HttpServletResponse)messageContext.get(MessageContext.SERVLET_RESPONSE));
        //super.setPrivateKey(StringUtil.trim(config.get(Environment.privateKey)));
        ConnectApp connectApp = connectAppDAO.loadConnectApp(appId);
        if (connectApp==null||connectApp.getId()<=0 || StringUtil.isNULL(verify))
        {
                return TXWebUtil.createRocErrorInfo("login",-32602,language.getLang(LanguageRes.notDataFind)+":appId").toString(4);
        }

        if (StringUtil.isNULL(connectApp.getSecret())||connectApp.getAuditingType()== AuditingType.NO)
        {
            return TXWebUtil.createRocErrorInfo("login",-32602,language.getLang(LanguageRes.interfaceClosed)+":auditing").toString(4);
        }

        //为验证做准备begin
        super.setPublicKey(TXWeb.publicKey);
        super.setLoginId(loginId);
        super.setPassword(password);
        super.setPrivateKey(connectApp.getSecret());
        super.setPassword(password);
        super.setPasswordEncoded(passwordEncoded);
        super.setTimeMillis(verifySecond);
        //为验证做准备end

        //    return  TXWebUtil.getLoginHashVerify(uid, loginId, publicKey, privateKey, password, passwordEncoded, timeMillis);
        String makeVerify = super.getMakeVerify();
        if (makeVerify.equals(verify))
        {
            return TXWebUtil.createRocErrorInfo("login",-32602,language.getLang(LanguageRes.validationFailure)).toString(4);
        }


        String ip = RequestUtil.getRemoteAddr(getRequest());
        JSONObject loginInfo ;
        try {
            loginInfo = onlineManager.login(session,loginId, password,passwordEncoded,"app:" + connectApp.getId(), ip);
        } catch (Exception e) {
            log.error(MessageFormatter.format("uid {} loginId {}", field, loginId).getMessage(), e);
            return TXWebUtil.createRocErrorInfo("applogin",-32099,"调用失败").toString();
        }
        return loginInfo.toString(4);
    }

    /**
     * 方法调用接口
     * @param call 可以是json或者xml的调用,格式看ROC
     * @return 返回调用结果，调用权限和系统配置权限绑定
     */
    @WebMethod
    public String process(String call)
    {
        MessageContext messageContext = wsContext.getMessageContext();
        setRequest((HttpServletRequest)messageContext.get(MessageContext.SERVLET_REQUEST));
        setResponse((HttpServletResponse)messageContext.get(MessageContext.SERVLET_RESPONSE));

        ///////////////////////////////////环境参数 begin
        Map<String, Object> envParams = createEnvironment();
        envParams.put(ActionSupport.Key_RealPath, Dispatcher.getRealPath());
        envParams.put(ActionSupport.Key_Request, request);
        envParams.put(ActionSupport.Key_Response, response);
        envParams.put(TXWeb.sessionId,sessionId);
        //setSessionId(sessionId);
        session.setAttribute(TXWeb.sessionId,sessionId);
        ///////////////////////////////////环境参数 end
        ///////////////////读取ajax请求 end
        if (StringUtil.isNULL(call))
        {
            return TXWebUtil.createRocErrorInfo("process",-32600, "Invalid Request.无效的请求").toString(4);
        }
        //////////////////初始begin

        if (call.length()>20480)
        {
            return TXWebUtil.createRocErrorInfo("process",-32602, "Invalid params.参数无效").toString(4);
        }

        //判断是XML还是JSON begin
        String rpc = StringUtil.trim(call);
        JSONObject jsonData = null;

        if (StringUtil.isXml(rpc))
        {
            //XML格式
            //蒋XML转化到JSON
            try {
                jsonData = XML.toJSONObject(rpc);
            } catch (JSONException e) {
                return TXWebUtil.createRocErrorInfo("process",-32600, "Invalid Request.无效的请求").toString(4);
            }
        }
        if (StringUtil.isJsonObject(rpc))
        {
            //JSON格式
            try {
                jsonData = new JSONObject(rpc);
            } catch (JSONException e) {
                return TXWebUtil.createRocErrorInfo("process",-32600, "Invalid Request.无效的请求").toString(4);
            }
        }
        if (jsonData==null)
        {
            return TXWebUtil.createRocErrorInfo("process",-32700, "Parse error.解析错误,不能识别的格式").toString(4);
        }
        //判断是XML还是JSON end
        String requestedWith = jsonData.getString(Environment.rocSecret);
        if (requestedWith.contains(Environment.rocSecret)) {
            //这种方式主要是返回Rsa的结果
            RsaRocService rsaRocService = new RsaRocService();
            try {
                return rsaRocService.doing(request,response,jsonData.toString());
            } catch (Exception e) {
                e.printStackTrace();
                return TXWebUtil.createRocErrorInfo("process",-32700, "Parse error.调用发生异常").toString(4);
            }
        } else {
            RocService rocService = new RocService();
            try {
                return rocService.doing(request,response,jsonData.toString());
            } catch (Exception e) {
                e.printStackTrace();
                return TXWebUtil.createRocErrorInfo("process",-32700, "Parse error.调用发生异常").toString(4);
            }
        }

    }

}
